`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/

/**
	@file MMU.v
	@author Andrew D. Zonenberg
	@brief Memory management unit
	
	TODO: figure out how to handle burst reads/writes for an entire cache line (or more)
	to flash or SDRAM
 */
module MMU(
	clk,
	mmu_addr, mmu_din, mmu_dout, mmu_wr, mmu_rd, mmu_wmask, mmu_dinok,
	norflash_addr, norflash_din, norflash_dout, norflash_wr, norflash_rd, norflash_wmask, norflash_dinok,
	uart_addr,   uart_din,  uart_dout,  uart_wr,  uart_rd,  uart_wmask,  uart_dinok,
	 led_addr,    led_din,   led_dout,   led_wr,   led_rd,   led_wmask,   led_dinok,
	 btn_addr,    btn_din,   btn_dout,   btn_wr,   btn_rd,   btn_wmask,   btn_dinok,	 
	 ram_addr,    ram_din,   ram_dout,   ram_wr,   ram_rd,   ram_wmask,   ram_dinok
    );

	////////////////////////////////////////////////////////////////////////////////////////////////
	// Global
	input wire clk;

	////////////////////////////////////////////////////////////////////////////////////////////////
	// L2 cache
	
	input wire[31:0] mmu_addr;
	input wire[31:0] mmu_din;
	output reg[31:0] mmu_dout = 0;
	input wire mmu_wr;
	input wire mmu_rd;
	input wire[3:0] mmu_wmask;
	output reg mmu_dinok = 0;
	
	reg mmu_rd_buf = 0;
	always @(posedge clk)
		mmu_rd_buf <= mmu_rd;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// NOR flash for boot loader
	
	output wire[31:0] norflash_addr;
	output wire[31:0] norflash_din;
	input wire[31:0] norflash_dout;
	output wire[3:0] norflash_wmask;
	output wire norflash_wr;
	output wire norflash_rd;
	input wire norflash_dinok;
	
	wire norflash_cs;
	wire norflash_cs_kseg0;
	wire norflash_cs_kseg1;
	
	assign norflash_cs_kseg0 = (mmu_addr[31:20] >= 12'hBFB) && (mmu_addr[31:20] <= 12'hBFF);
	assign norflash_cs_kseg1 = (mmu_addr[31:20] >= 12'h9FB) && (mmu_addr[31:20] <= 12'h9FF);
	
	assign norflash_cs = norflash_cs_kseg0 | norflash_cs_kseg1;
	assign norflash_addr = norflash_cs ? {9'h0, mmu_addr[22:0]} : 32'h0;
	assign norflash_din = norflash_cs ? mmu_din : 1'b0;
	assign norflash_wr = norflash_cs ? mmu_wr : 1'b0;
	assign norflash_rd = norflash_cs ? mmu_rd : 1'b0;
	assign norflash_wmask = norflash_cs ? mmu_wmask : 4'b0;

	reg norflash_cs_buf = 0;
	always @(posedge clk)
		norflash_cs_buf <= norflash_cs;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// UART
	
	output wire[31:0] uart_addr;
	output wire[31:0] uart_din;
	input wire[31:0] uart_dout;
	output wire[3:0] uart_wmask;
	output wire uart_wr;
	output wire uart_rd;
	input wire uart_dinok;
	
	wire uart_cs;
	assign uart_cs = (mmu_addr[31:8] == 24'hb00000);
	assign uart_addr = uart_cs ? {24'h0, mmu_addr[7:0]} : 32'h0;
	assign uart_din = uart_cs ? mmu_din : 1'b0;
	assign uart_wr = uart_cs ? mmu_wr : 1'b0;
	assign uart_rd = uart_cs ? mmu_rd : 1'b0;
	assign uart_wmask = uart_cs ? mmu_wmask : 4'b0;

	reg uart_cs_buf = 0;
	always @(posedge clk)
		uart_cs_buf <= uart_cs;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// LED bank
	output wire[31:0] led_addr;
	output wire[31:0] led_din;
	input wire[7:0] led_dout;
	output wire[3:0] led_wmask;
	output wire led_wr;
	output wire led_rd;
	input wire led_dinok;
	
	wire led_cs;
	assign led_cs = (mmu_addr[31:8] == 24'hb00001);
	assign led_addr = led_cs ? {24'h0, mmu_addr[7:0]} : 32'h0;
	assign led_din = led_cs ? mmu_din : 1'b0;
	assign led_wr = led_cs ? mmu_wr : 1'b0;
	assign led_rd = led_cs ? mmu_rd : 1'b0;
	assign led_wmask = led_cs ? mmu_wmask : 4'b0;

	reg led_cs_buf = 0;
	always @(posedge clk)
		led_cs_buf <= led_cs;
		
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Button array
	output wire[31:0] btn_addr;
	output wire[31:0] btn_din;
	input wire[7:0] btn_dout;
	output wire[3:0] btn_wmask;
	output wire btn_wr;
	output wire btn_rd;
	input wire btn_dinok;
	
	wire btn_cs;
	assign btn_cs = (mmu_addr[31:8] == 24'hb00002);
	assign btn_addr = btn_cs ? {24'h0, mmu_addr[7:0]} : 32'h0;
	assign btn_din = btn_cs ? mmu_din : 1'b0;
	assign btn_wr = btn_cs ? mmu_wr : 1'b0;
	assign btn_rd = btn_cs ? mmu_rd : 1'b0;
	assign btn_wmask = btn_cs ? mmu_wmask : 4'b0;

	reg btn_cs_buf = 0;
	always @(posedge clk)
		btn_cs_buf <= btn_cs;
		
	////////////////////////////////////////////////////////////////////////////////////////////////
	// RAM
	output wire[31:0] ram_addr;
	output wire[31:0] ram_din;
	input wire[31:0] ram_dout;
	output wire[3:0] ram_wmask;
	output wire ram_wr;
	output wire ram_rd;
	input wire ram_dinok;
	
	wire ram_cs_kuseg;
	assign ram_cs_kuseg = (mmu_addr[31:26] == 6'b000000);			//Cached RAM (kuseg)
	wire ram_cs_kseg0;
	assign ram_cs_kseg0 = (mmu_addr[31:26] == 6'b100000);			//Cached ram (kseg0)
	wire ram_cs_kseg1;
	assign ram_cs_kseg1 = (mmu_addr[31:26] == 6'b101000);			//Uncached ram (kseg1)
	
	wire ram_cs;
	assign ram_cs = ram_cs_kuseg | ram_cs_kseg0 | ram_cs_kseg1;
	assign ram_addr = ram_cs ? {6'b0, mmu_addr[25:0]} : 32'h0;
	assign ram_din = ram_cs ? mmu_din : 1'b0;
	assign ram_wr = ram_cs ? mmu_wr : 1'b0;
	assign ram_rd = ram_cs ? mmu_rd : 1'b0;
	assign ram_wmask = ram_cs ? mmu_wmask : 4'b0;

	reg ram_cs_buf = 0;
	always @(posedge clk)
		ram_cs_buf <= ram_cs;
		
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Muxing for reads
	
	always @(
		mmu_rd_buf,
		led_cs_buf, led_dout, led_dinok,
		btn_cs_buf, btn_dout, btn_dinok,
		uart_cs_buf, uart_dout, uart_dinok,
		norflash_cs_buf, norflash_dout, norflash_dinok,
		ram_cs_buf, ram_dout, ram_dinok
		) begin
		
		mmu_dout <= 0;
		mmu_dinok <= 0;
		
		//one-hot coding, no else's needed
		if(mmu_rd_buf) begin
			if(led_cs_buf) begin
				mmu_dout <= {24'b0, led_dout};
				mmu_dinok <= led_dinok;
			end
			
			if(btn_cs_buf) begin
				mmu_dout <= {24'b0, btn_dout};
				mmu_dinok <= btn_dinok;
			end
			
			if(uart_cs_buf) begin
				mmu_dout <= uart_dout;
				mmu_dinok <= uart_dinok;
			end
			
			if(norflash_cs_buf) begin
				mmu_dout <= norflash_dout;
				mmu_dinok <= norflash_dinok;
			end
			
			if(ram_cs_buf) begin
				mmu_dout <= ram_dout;
				mmu_dinok <= ram_dinok;
			end
		end
	end
	

endmodule
